package nachos.threads;

import java.util.Iterator;
import java.util.LinkedList;

import nachos.machine.*;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>, and multiple
 * threads can be waiting to <i>listen</i>. But there should never be a time
 * when both a speaker and a listener are waiting, because the two threads can
 * be paired off at this point.
 */
public class Communicator {
	/**
	 * Allocate a new communicator.
	 */
	public Communicator() {

		lock = new Lock();
		empty = true;
		speaker = new Condition2(lock);
		listener = new Condition2(lock);
		wordQueue = new LinkedList<Integer>();
	}

	/**
	 * Wait for a thread to listen through this communicator, and then transfer
	 * <i>word</i> to the listener.
	 * 
	 * <p>
	 * Does not return until this thread is paired up with a listening thread.
	 * Exactly one listener should receive <i>word</i>.
	 * 
	 * @param word
	 *            the integer to transfer.
	 */
	public void speak(int word) {
		lock.acquire();
		wordQueue.add(word);
		while (li == 0) {
			speaker.sleep();
		}

		empty = false;
//		System.out.println("PRINT QUEUE");
//	    for (Iterator i=wordQueue.iterator(); i.hasNext(); )
//			System.out.println((Integer) i.next() + " ");
		
		listener.wake();

		lock.release();
	}

	/**
	 * Wait for a thread to speak through this communicator, and then return the
	 * <i>word</i> that thread passed to <tt>speak()</tt>.
	 * 
	 * @return the integer transferred.
	 */
	public int listen() {
		lock.acquire();

		li++;

		while (wordQueue.isEmpty()) {
			listener.sleep();
		}
		
		speaker.wake();

		empty = false;
		int what_word = 0;

		if (!wordQueue.isEmpty()) {
			what_word = wordQueue.pop();
		}

		lock.release();
		return what_word;
	}

	private Lock lock;
	private boolean empty;
	private LinkedList<Integer> wordQueue;
	private int li;
	private Condition2 speaker;
	private Condition2 listener;

}
